package com.liusr.template.interceptor;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.liusr.template.common.security.JwtToken;
import com.liusr.template.common.security.LoginCheck;
import com.liusr.template.constant.BaseConstants;
import com.liusr.template.constant.StringConstants;
import com.liusr.template.exception.CustomBusinessException;
import com.liusr.template.exception.NoLoginException;
import com.liusr.template.exception.ParamErrorException;
import com.liusr.template.exception.TokenErrorException;
import com.liusr.template.util.JsonUtils;
import com.liusr.template.util.UrlUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;

/**
 * @author Liusr
 * @date 2023/1/23
 * @package com.liusr.template.interceptor
 * @Description: 登录校验拦截器
 */
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		System.out.println();
		log.debug("进入拦截器....");
		return this.hasLogin(request, handler);
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		log.debug("退出拦截器....");
	}

	/**
	 * 登录校验
	 *
	 * @param request request
	 * @param handler handler
	 * @return boolean
	 */
	private boolean hasLogin(HttpServletRequest request, Object handler) {

		if (handler instanceof HandlerMethod) {
			log.info(StringConstants.LOGIN_CHECK_START);
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			//获取当前请求的URL
			String servletPath = UrlUtils.getUrl(request);
			//获取方法上的【登录校验】注解
			LoginCheck loginCheckAnnotation = handlerMethod.getMethod().getAnnotation(LoginCheck.class);

			//方法没有【登录校验】注解，直接放行
			if (loginCheckAnnotation == null) {
				log.info(StringConstants.LOGIN_CHECK_IS_NO + "【" + servletPath + "】");
				log.info(StringConstants.LOGIN_CHECK_END);
				return true;
			}

			// TODO 这里可以对注解中第四个参数进行校验，传入的是请求方法，默认是POST

			//方法有【登录校验】注解，并且不要校验Token，直接放行
			if (!loginCheckAnnotation.isToken()) {
				log.info(StringConstants.LOGIN_CHECK_IS_NO + "【" + servletPath + "】");
				log.info(StringConstants.LOGIN_CHECK_END);
				return true;
			}
			//方法有【登录校验】注解，并且不必须包含参数，直接放行
			if (!loginCheckAnnotation.isParam()) {
				log.info(StringConstants.LOGIN_CHECK_IS_NO + "【" + servletPath + "】");
				log.info(StringConstants.LOGIN_CHECK_END);
				return true;
			}

			log.info(StringConstants.LOGIN_CHECK_START_TIME + LocalDateTime.now());
			log.info(StringConstants.LOGIN_CHECK_URL + "【" + servletPath + "】");

			//获取传进来的参数jsonStr
			String jsonStr = request.getParameter(BaseConstants.JSON_STR);
			if (StrUtil.isBlank(jsonStr)) {
				log.info(StringConstants.LOGIN_CHECK_PARAM);
				log.info(StringConstants.LOGIN_CHECK_END_TIME + LocalDateTime.now());
				log.info(StringConstants.LOGIN_CHECK_END);
				throw new ParamErrorException(StringConstants.EXCEPTION_PARAM_ERROR);
			}

			//字符串转换成JSONObject对象
			JSONObject jsonObject = JsonUtils.strToJson(jsonStr);

			//获取JSONObject对象中的Token键
			String token = (String) jsonObject.get(BaseConstants.PC_USER_TOKEN);

			//判断是否有Token，没有说明未登录
			if (StrUtil.isBlank(token) && StrUtil.isBlank(token = (String) jsonObject.get(BaseConstants.PC_USER_TOKEN_))) {
				log.info(StringConstants.LOGIN_CHECK_FAILED);
				log.info(StringConstants.LOGIN_CHECK_END_TIME + LocalDateTime.now());
				log.info(StringConstants.LOGIN_CHECK_END);
				throw new NoLoginException(StringConstants.EXCEPTION_NO_LOGIN_TOKEN);
			}

			//有token，并对比Token和Session中的Token是否一致，不一致
			if (!JwtToken.judgeTokenIsExitsAndCorrect(token, request)) {
				log.info(StringConstants.LOGIN_CHECK_TOKEN_FAILED);
				log.info(StringConstants.LOGIN_CHECK_END_TIME + LocalDateTime.now());
				log.info(StringConstants.LOGIN_CHECK_END);
				throw new TokenErrorException(StringConstants.LOGIN_CHECK_TOKEN_FAILED);
			}

			log.info(StringConstants.LOGIN_CHECK_SUCCESS + token);
			log.info(StringConstants.LOGIN_CHECK_END_TIME + LocalDateTime.now());
			log.info(StringConstants.LOGIN_CHECK_END);
			return true;
		} else {
			log.error(StringConstants.EXCEPTION_URL_NO_EXITS);
			throw new CustomBusinessException(StringConstants.EXCEPTION_URL_NO_EXITS);
			//return false;
		}
	}
}
